# /code/main.py
import asyncio
import os
import signal
import sys
from pathlib import Path
from code.logger_config import logger
from code.config import PROJECT_TITLE
from code.browser_ops import launch_browser, login, fetch_page_with_browser
from code.crawler import user_tree_monitor, fetch_page_with_http, process_unknown_gender_position
from code.test_environment import test_environment


class ContainerOptimizedApp:
    """容器优化的应用程序类"""

    def __init__(self):
        self.browser = None
        self.is_container = self._detect_container_environment()
        self.shutdown_requested = False
        self._setup_signal_handlers()
        self._setup_environment()

    def _detect_container_environment(self):
        """检测是否在容器环境中运行"""
        container_indicators = [
            os.path.exists('/.dockerenv'),
            os.environ.get('CONTAINER') == 'true',
            os.environ.get('KUBERNETES_SERVICE_HOST') is not None,
            Path('/proc/1/cgroup').exists() and 'docker' in Path('/proc/1/cgroup').read_text() if Path(
                '/proc/1/cgroup').exists() else False
        ]

        is_container = any(container_indicators)
        if is_container:
            logger.info("🐳 检测到容器环境")
        else:
            logger.info("💻 检测到本地环境")

        return is_container

    def _setup_signal_handlers(self):
        """设置信号处理器以优雅关闭"""

        def signal_handler(signum, frame):
            logger.info(f"📡 收到信号 {signum}，准备优雅关闭...")
            self.shutdown_requested = True

        # 注册信号处理器
        signal.signal(signal.SIGTERM, signal_handler)
        signal.signal(signal.SIGINT, signal_handler)
        if hasattr(signal, 'SIGHUP'):
            signal.signal(signal.SIGHUP, signal_handler)

    def _setup_environment(self):
        """设置环境配置"""
        # 确保日志目录存在
        log_dir = Path('/app/logs') if self.is_container else Path('./logs')
        log_dir.mkdir(parents=True, exist_ok=True)

        # 容器环境特定配置
        if self.is_container:
            # 设置容器环境变量
            os.environ.setdefault('PYTHONUNBUFFERED', '1')
            os.environ.setdefault('PYTHONIOENCODING', 'utf-8')

            # 优化内存使用
            if 'MALLOC_ARENA_MAX' not in os.environ:
                os.environ['MALLOC_ARENA_MAX'] = '2'

        logger.info(f"📁 日志目录: {log_dir.absolute()}")
        logger.info(f"🔧 容器模式: {'启用' if self.is_container else '禁用'}")

    async def cleanup_browser(self):
        """清理浏览器资源"""
        if self.browser:
            try:
                logger.info("🧹 正在清理浏览器资源...")
                await self.browser.close()
                logger.success("✅ 浏览器资源清理完成")
            except Exception as e:
                logger.warning(f"⚠️ 浏览器清理时出现警告: {str(e)}")
            finally:
                self.browser = None

    async def test_js_page(self):
        """测试需要JS分析的页面"""
        test_url = "http://172.18.0.17/module/user_select/item.php?ACTION=online"
        logger.info(f"开始测试JS页面: {test_url}")

        content = await fetch_page_with_browser(self.browser, test_url)
        if content:
            logger.success(f"获取到JS页面内容，长度: {len(content)}")
            if os.getenv('DEBUG_MODE') == 'True':
                logger.debug(f"页面内容片段:\n{content[:500]}...")
        else:
            logger.error("JS页面测试失败")

    async def test_http_page(self):
        """测试不需要JS分析的页面"""
        test_url = "http://172.18.0.17/module/user_select/item.php?ACTION=online"
        logger.info(f"开始测试HTTP页面: {test_url}")

        content = await fetch_page_with_http(test_url)
        if content:
            logger.success(f"获取到HTTP页面内容，长度: {len(content)}")
            if os.getenv('DEBUG_MODE') == 'True':
                logger.debug(f"页面内容片段:\n{content[:500]}...")
        else:
            logger.error("HTTP页面测试失败")

    async def initialize_browser_with_retry(self, max_retries=3):
        """带重试机制的浏览器初始化"""
        for attempt in range(max_retries):
            try:
                logger.info(f"🚀 尝试初始化浏览器 (第{attempt + 1}次)")
                self.browser = await launch_browser()
                logger.success("✅ 浏览器初始化成功")
                return True
            except Exception as e:
                logger.error(f"❌ 浏览器初始化失败 (第{attempt + 1}次): {str(e)}")
                if attempt < max_retries - 1:
                    wait_time = (attempt + 1) * 5
                    logger.info(f"⏳ 等待 {wait_time} 秒后重试...")
                    await asyncio.sleep(wait_time)
                else:
                    logger.error("❌ 浏览器初始化最终失败")
                    return False
        return False

    async def login_with_retry(self, max_retries=3):
        """带重试机制的登录"""
        for attempt in range(max_retries):
            try:
                logger.info(f"🔐 尝试登录系统 (第{attempt + 1}次)")
                await login(self.browser)
                logger.success("✅ 登录成功")
                return True
            except Exception as e:
                logger.error(f"❌ 登录失败 (第{attempt + 1}次): {str(e)}")
                if attempt < max_retries - 1:
                    wait_time = (attempt + 1) * 3
                    logger.info(f"⏳ 等待 {wait_time} 秒后重试...")
                    await asyncio.sleep(wait_time)
                else:
                    logger.error("❌ 登录最终失败")
                    return False
        return False

    async def run_environment_tests(self):
        """执行环境测试 - 修复为异步函数"""
        logger.info("🔍 开始环境测试...")
        try:
            # 检查test_environment是否为协程函数
            import inspect
            if inspect.iscoroutinefunction(test_environment):
                env_test_result = await test_environment()
            else:
                env_test_result = test_environment()

            if not env_test_result:
                logger.warning("⚠️ 环境测试部分项目未通过，但程序将继续运行")
            else:
                logger.success("✅ 环境测试通过")

            return True  # 总是返回True，让程序继续运行
        except Exception as e:
            logger.error(f"❌ 环境测试异常: {str(e)}")
            return True  # 即使异常也继续运行

    async def schedule_tasks(self):
        """定时任务调度器 - 容器优化版本"""
        count = 0
        consecutive_errors = 0
        max_consecutive_errors = 5

        while not self.shutdown_requested:
            try:
                # 检查浏览器状态 - 修复process属性检查
                if not self.browser:
                    logger.warning("⚠️ 检测到浏览器为空，尝试重新初始化...")
                    if not await self.initialize_browser_with_retry():
                        logger.error("❌ 浏览器重新初始化失败，程序退出")
                        break
                    if not await self.login_with_retry():
                        logger.error("❌ 重新登录失败，程序退出")
                        break
                elif hasattr(self.browser, 'process') and self.browser.process and hasattr(self.browser.process,
                                                                                           'returncode') and self.browser.process.returncode is not None:
                    logger.warning("⚠️ 检测到浏览器进程异常，尝试重新初始化...")
                    await self.cleanup_browser()
                    if not await self.initialize_browser_with_retry():
                        logger.error("❌ 浏览器重新初始化失败，程序退出")
                        break
                    if not await self.login_with_retry():
                        logger.error("❌ 重新登录失败，程序退出")
                        break

                # 执行人员监控
                logger.info("👥 开始执行人员监控任务...")
                await user_tree_monitor(self.browser)
                logger.success("✅ 人员监控任务完成")

                # 重置错误计数
                consecutive_errors = 0

                # 每2小时执行未知数据处理
                count += 1
                if count >= 24:  # 24 * 5分钟 = 2小时
                    logger.info("🔄 开始执行未知数据处理任务...")
                    await process_unknown_gender_position()
                    logger.success("✅ 未知数据处理任务完成")
                    count = 0

                # 等待5分钟
                wait_for_minutes = 5
                logger.info(f"⏰ 任务完成，等待 {wait_for_minutes} 分钟后继续...")

                # 分段等待，以便响应关闭信号
                for i in range(wait_for_minutes * 60):
                    if self.shutdown_requested:
                        logger.info("📡 收到关闭信号，退出等待循环")
                        break
                    await asyncio.sleep(1)

            except Exception as e:
                consecutive_errors += 1
                logger.error(f"❌ 定时任务执行出错 (连续错误: {consecutive_errors}/{max_consecutive_errors}): {str(e)}")

                if consecutive_errors >= max_consecutive_errors:
                    logger.error(f"❌ 连续错误次数达到上限 ({max_consecutive_errors})，程序退出")
                    break

                # 错误后等待更长时间
                error_wait_time = min(consecutive_errors * 30, 300)  # 最多等待5分钟
                logger.info(f"⏳ 错误后等待 {error_wait_time} 秒...")
                for i in range(error_wait_time):
                    if self.shutdown_requested:
                        break
                    await asyncio.sleep(1)

        logger.info("🔚 定时任务调度器退出")

    async def run_tests(self):
        """运行测试用例"""
        if os.getenv('RUN_TESTS') == 'True':
            logger.info("🧪 开始执行测试用例...")
            try:
                await self.test_js_page()
                await self.test_http_page()
                logger.success("✅ 测试用例执行完成")
            except Exception as e:
                logger.error(f"❌ 测试用例执行失败: {str(e)}")

    async def main(self):
        """主函数，协调各模块工作"""
        try:
            logger.success(f"🎯 {PROJECT_TITLE}")
            logger.info(f"🐍 Python版本: {sys.version}")
            logger.info(f"📍 工作目录: {os.getcwd()}")

            # 执行环境测试 - 修复为异步调用
            if not await self.run_environment_tests():
                return False

            # 初始化浏览器
            if not await self.initialize_browser_with_retry():
                return False

            # 登录系统
            if not await self.login_with_retry():
                return False

            # 等待系统稳定
            await asyncio.sleep(3)

            # 初始执行一次监控任务
            logger.info("🚀 执行初始监控任务...")
            await user_tree_monitor(self.browser)

            # 更新数据库职位及性别
            logger.info("🔄 执行初始数据处理...")
            await process_unknown_gender_position()

            # 运行测试（如果启用）
            await self.run_tests()

            # 启动定时任务
            logger.info("⏰ 启动定时任务调度器...")
            await self.schedule_tasks()

            logger.info("✅ 程序正常结束")
            return True

        except Exception as e:
            logger.error(f"❌ 程序运行出错: {str(e)}")
            return False
        finally:
            await self.cleanup_browser()


async def main():
    """程序入口点"""
    app = ContainerOptimizedApp()
    success = await app.main()

    if app.is_container:
        # 容器环境中，根据执行结果设置退出码
        exit_code = 0 if success else 1
        logger.info(f"🏁 程序退出，退出码: {exit_code}")
        sys.exit(exit_code)
    else:
        # 本地环境中，保持原有行为
        if os.getenv('DEBUG_MODE') == 'True':
            logger.debug("完成程序，等待3秒后结束")
            await asyncio.sleep(3)


if __name__ == '__main__':
    # 设置事件循环策略（Windows兼容性）
    if sys.platform == 'win32':
        asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())

    # 创建并设置事件循环
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    try:
        loop.run_until_complete(main())
    except KeyboardInterrupt:
        logger.info("👋 收到键盘中断信号，程序退出")
    except Exception as e:
        logger.error(f"❌ 程序运行出错: {str(e)}")
        sys.exit(1)
    finally:
        # 清理事件循环
        try:
            loop.close()
        except Exception:
            pass